<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace App\Components;
use Log;
/**
 * Description of HttpRequest
 *
 * @author Xuebing
 */
class HttpRequest {
    
    const METHOD_GET = 'GET';
    const METHOD_POST = 'POST';
    const METHOD_PUT = 'PUT';
    const METHOD_DELETE = 'DELETE';
    const METHOD_PATCH = 'PATCH';
    
    private $_host = '';
    private $_route = '';
    private $_method = '';
    private $_data = [];
    private $_headers = null;
    
    /**
     * 发送一条http请求
     * @param string $str_Method 请求方法
     * @param string $str_Host 域名
     * @param string $str_Route 路由
     * @param array $map_Data 参数列表
     * @param bool $boo_AutoSend 是否发送
     * @return string
     */
    public static function createHttpRequest($str_Method,$str_Host,$str_Route,$map_Data,$boo_AutoSend = false)
    {
        $request = new static();
        $request->setMethod($str_Method)
            ->setHost($str_Host)
            ->setRoute($str_Route);
        if (is_array($map_Data)) {
            foreach ($map_Data as $key => $value) {
                $request->setValue($key, $value);
            }
        }
        if($boo_AutoSend){
            return $request->send();
        }
    }
    
    public static function get($str_Host,$str_Route,$map_Data){
        return static::createHttpRequest(static::METHOD_GET, $str_Host, $str_Route, $map_Data,true);
    }
    public static function post($str_Host,$str_Route,$map_Data){
        return static::createHttpRequest(static::METHOD_POST, $str_Host, $str_Route, $map_Data,true);
    }
    
    public function setValue($key,$value){
        $this->_data[$key] = $value;
        return $this;
    }
    
    public function setMethod($method){
        $this->_method = $method;
        return $this;
    }
    
    public function send()
    {
        $ret = '';
        if (!$this->_checkMethod()) {
            throwException(new \Exception(''));
        }
        Log::info('Do http request: '.$this->_method . ' ' . $this->_getUrl() . ' ' . $this->_getParamStr());
        if($this->_method === static::METHOD_GET){
           $ret = $this->_doGet();
        } else {
            $ret = $this->_doRequest();
        }
        Log::info('Response: ' . print_r($ret, true));
        $this->_clearData();
        return new HttpRequestResult($ret);
    }
    
    public function setHost($host){
        $this->_host = $host;
        return $this;
    }
    
    public function setRoute($route){
        $this->_route = $route;
        return $this;
    }
    
    public function setHeaders($headers){
        $this->_headers = $headers;
    }
    
    public function getUrl(){
        return $this->_getUrl();
    }
    
    private function _getUrl(){
        return 'http://' . $this->_host . '/' . $this->_route;
    }
    
    private function _doGet(){
//        $ch = curl_init();
//        curl_setopt($ch,CURLOPT_URL,$this->_getUrl() . '?' . $this->_getParamStr());
//        Log::info($this->_getUrl() . '?' . $this->_getParamStr());
//        curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
//        curl_setopt($ch,CURLOPT_HEADER,0);
//        $str_Content = curl_exec($ch);
//            curl_close($ch);
        $str_Content = @file_get_contents($this->_getUrl() . '?' . $this->_getParamStr());
        if(isset($http_response_header) && $http_response_header){
            $str_Status = array_shift($http_response_header);
            $int_StatusCode = explode(' ', $str_Status)[1];
            $arr_Header = $this->parseHeader($http_response_header);
        }else{
            $int_StatusCode = 404;
            $arr_Header = [];
        }
        return [
            'content' => $str_Content,
            'status_code' => $int_StatusCode,
            'header' => $this->parseHeader($arr_Header)
        ];
    }
    
    private function _doRequest(){
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->_getUrl());
        switch ($this->_method){
            case "POST": 
                curl_setopt($ch, CURLOPT_POST,true);
                break;  
            case "PUT" : 
                curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "PUT");
                break;  
            case "DELETE":
                curl_setopt ($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
                break;
//            case "GET":
//                curl_setopt($ch, C,false);
//                break;
        }
        if($this->_headers !== null){
            curl_setopt($ch, CURLOPT_HTTPHEADER, $this->_headers);
        }
        curl_setopt($ch, CURLOPT_POSTFIELDS,$this->_getParamStr());
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HEADER, true);

        $sContent = curl_exec($ch);
        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $int_StatusCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
        curl_close($ch);
        $arr_Header = explode("\r\n",trim(substr($sContent, 0, $headerSize)));
        array_shift($arr_Header);
        return [
            'content' => substr($sContent, $headerSize),
            'status_code' => $int_StatusCode,
            'header' => $this->parseHeader($arr_Header)
        ];
    }
    
    private function parseHeader($arr_Header){
        $ret = [];
//        array_shift($arr_Header);
        foreach($arr_Header as $header){
            $tmp_Header = explode(':', $header);
            if(count($tmp_Header) != 2){
                continue;
            }
            $ret[$tmp_Header[0]] = trim($tmp_Header[1]);
        }
        return $ret;
    }
    
    private function _getParamStr(){

        return http_build_query($this->_data);
      /*  $arr_PostTemp = [];
        foreach($this->_data as $k => $v){
            $arr_PostTemp[] = $this->_getParamIteration(urlencode($k),$v);
        }
        return implode('&', $arr_PostTemp);*/
    }
    
    private function _getParamIteration($key,$param){
        if(is_array($param)){
            $arr_Param = [];
            foreach($param as $k1 => $v1){
                $arr_Param[] = $this->_getParamIteration($key . '[' . urlencode($k1) . ']', $v1);
            }
            return implode('&', $arr_Param);
        }else{
            return $key . '=' . $param;
        }
    }
    
    private function _clearData(){
        $this->_data = [];
    }
    
    private function _checkMethod(){
        return in_array($this->_method, [
            static::METHOD_GET,
            static::METHOD_POST,
            static::METHOD_PUT,
            static::METHOD_PATCH,
            static::METHOD_DELETE
        ]);
    }
}
